<?php

namespace App\Services;

use App\Common\Enums\ExceptionTypeEnum;
use App\Common\Jobs\CreateErrorLogJob;
use App\Common\Services\BaseService;
use App\Common\Tools\CustomException;
use App\Models\FeishuCallbackEventModel;
use App\Models\FeishuEmployeeModel;
use App\Models\FeishuTenantAccessTokenModel;
use App\Sdks\Feishu\Feishu;

class FeishuService extends BaseService
{
    protected $sdk;

    /**
     * @var
     * 员工列表
     */
    protected $employees;

    /**
     * constructor.
     */
    public function __construct(){
        parent::__construct();

        $this->sdk = new Feishu(env('FEISHU_APP_ID'), env('FEISHU_APP_SECRET'));
    }

    /**
     * @param $token
     * @return bool
     * 验证 token
     */
    private function checkEventToken($token){
        return $token == env('FEISHU_EVENT_VERIFICATION_TOKEN');
    }

    /**
     * @param $request
     * @return \Illuminate\Http\JsonResponse
     * @throws CustomException
     * 事件订阅
     */
    public function event($data){
        // 验证 token
        if($this->checkEventToken($data['token'])){

            // 订阅地址校验
            if(isset($data['challenge'])){
                return $this->eventReturn([
                    'challenge' => $data['challenge'],
                ]);
            }

            $feishuCallbackEventModel = new FeishuCallbackEventModel();
            $feishuCallbackEventModel->callback_type = $data['type'] ?? '';
            $feishuCallbackEventModel->event_type = $data['event']['type'] ?? '';
            $feishuCallbackEventModel->app_id = $data['event']['app_id'] ?? '';
            $feishuCallbackEventModel->tenant_key = $data['event']['tenant_key'] ?? '';
            $feishuCallbackEventModel->extends = $data['event'] ?? [];
            $feishuCallbackEventModel->save();

            // 回调事件类型
            $callbackType = $data['type'] ?? '';
            if($callbackType == 'event_callback'){
                $this->eventHandle($data['event']);
            }
        }else{
            // 无效token
            dispatch(new CreateErrorLogJob(
                'EVENT_REQUEST_ERROR_LOG',
                '事件请求错误日志',
                $data,
                ExceptionTypeEnum::CUSTOM)
            );
        }
    }

    /**
     * @param $data
     * @return \Illuminate\Http\JsonResponse
     * 事件公共返回
     */
    public function eventReturn($data){
        return response()->json($data);
    }

    /**
     * @param $event
     * @throws CustomException
     * 事件处理
     */
    private function eventHandle($event){
        // 设置 token
        $this->setTenantAccessToken();

        if($event['type'] == 'message'){
            // 消息事件
            $msgType = $event['msg_type'] ?? '';
            switch($msgType){
                case 'text':
                    $this->messageEventText($event);
                    break;
            }
        }else{
            // 其他事件
        }
    }

    /**
     * @param $event
     * @return bool
     * @throws CustomException
     * 文本消息事件
     */
    public function messageEventText($event){
        $keywordList = $this->getKeywordList();

        $text = trim($event['text_without_at_bot']);

        $keywords = array_map(function($value){
            return "#{$value}#";
        }, array_column($keywordList, 'id'));

        if(in_array($text, $keywords)){
            $replyText = $this->keywordHandle(trim($text, '#'));
        }else{
            $replyText = $this->getHelp();
        }

        // 回复
        if($event['chat_type'] == 'private'){
            // 私聊
            $this->sendMessageToOpenId($event['open_id'], '机器人私聊回复', $replyText);
        }elseif($event['chat_type'] == 'group'){
            // 群聊
            $employeeModel = new FeishuEmployeeModel();
            $employee = $employeeModel->where('open_id', $event['open_id'])->first();
            $replyName = $employee->name ?? '';
            $this->sendMessageToChatId($event['open_chat_id'], '机器人群聊回复', "<at open_id=\"{$event['open_id']}\">@{$replyName}</at> ". $replyText);
        }

        return true;
    }

    /**
     * @return array
     * 获取关键词列表
     */
    public function getKeywordList(){
        return [
            ['id' =>'hi', 'name' => '打招呼'],
            ['id' =>'time', 'name' => '查询当前时间'],
            ['id' =>'chp', 'name' => '彩虹屁'],
        ];
    }

    /**
     * @return string
     * 获取帮助
     */
    public function getHelp(){
        $tmp = [];
        $i = 1;
        foreach($this->getKeywordList() as $keyword){
            $tmp[] = "{$i}.{$keyword['name']}请输入:#{$keyword['id']}#";
            $i++;
        }
        $help = "你说的我不太懂,可以输入下列关键字,执行对应操作哦\n". implode("\n", $tmp);
        return $help;
    }

    /**
     * @param $keyword
     * @return false|string
     * 关键字处理
     */
    public function keywordHandle($keyword){
        if($keyword == 'hi'){
            return '你好呀~';
        }elseif($keyword == 'time'){
            return '现在是北京时间 '. date('Y-m-d H:i:s');
        }elseif($keyword == 'chp'){
            return file_get_contents('https://chp.shadiao.app/api.php');
        }else{
            return "sending {$keyword}...";
        }
    }

    /**
     * @return mixed
     * 获取员工列表
     */
    public function getEmployees(){
        if(empty($this->employees)){
            $feishuEmployeeModel = new FeishuEmployeeModel();
            $this->employees = $feishuEmployeeModel->get();
        }
        return $this->employees;
    }

    /**
     * @param $name
     * @param $title
     * @param $tags
     * @return bool
     * @throws CustomException
     * 发送消息
     */
    public function sendMessage($name, $title, $tags){
        // 员工映射
        $employees = $this->getEmployees();
        $employeeMap = $employees->pluck('open_id', 'name');

        // 富文本
        $posts = [];
        foreach($tags as $tag){
            // 标签转富文本
            $post = $this->tagToPost($tag);

            foreach($post as $k => $v){
                // at名称映射 user_id
                if($v['tag'] == 'at'){
                    $post[$k]['user_id'] = $employeeMap[$v['name']] ?? '';
                    unset($post[$k]['name']);
                }
            }

            $posts[] = $post;
        }

        $feishuEmployeeModel = new FeishuEmployeeModel();
        $employee = $feishuEmployeeModel->where('name', $name)
            ->first();

        if(empty($employee)){
            throw new CustomException([
                'code' => 'NOT_FOUND_EMPLOYEE',
                'message' => "找不到员工{{$name}}",
            ]);
        }

        $data = $this->sendMessageToOpenId($employee->open_id, $title, $posts);

        return $data;
    }

    /**
     * @param $openId
     * @param $title
     * @param $content
     * @return bool
     * @throws CustomException
     * 私聊发送消息
     */
    private function sendMessageToOpenId($openId, $title, $content){
        return $this->_sendMessage('open_id', $openId, $title, $content);
    }

    /**
     * @param $chatId
     * @param $title
     * @param $content
     * @return bool
     * @throws CustomException
     * 群聊发送消息
     */
    private function sendMessageToChatId($chatId, $title, $content){
        return $this->_sendMessage('chat_id', $chatId, $title, $content);
    }

    /**
     * @param $targetType
     * @param $targetId
     * @param $title
     * @param $content
     * @return bool
     * @throws CustomException
     * 发送消息
     */
    private function _sendMessage($targetType, $targetId, $title, $content){
        // 设置 token
        $this->setTenantAccessToken();

        if($targetType == 'open_id'){
            // 发送消息
            $data = $this->sdk->sendPostToOpenId($targetId, $title, $content);
        }elseif($targetType == 'chat_id'){
            $data = $this->sdk->sendPostToChatId($targetId, $title, $content);
        }else{
            throw new CustomException([
                'code' => 'TARGET_TYPE_ERROR',
                'message' => '目标类型错误',
            ]);
        }

        return $data;
    }

    /**
     * @return mixed
     * 设置 token
     */
    public function setTenantAccessToken(){
        $appId = $this->sdk->getAppId();
        $time = date('Y-m-d H:i:s', TIMESTAMP);

        $model = new FeishuTenantAccessTokenModel();
        // 数据库获取
        $item = $model->where('app_id', $appId)
            ->where('expired_at', '>', $time)
            ->orderBy('created_at', 'desc')
            ->first();

        if(empty($item->tenant_access_token)){
            // api获取
            $data = $this->sdk->getTenantAccessToken();

            $tenant_access_token = $data['tenant_access_token'];

            // 保存
            $model->app_id = $appId;
            $model->tenant_access_token = $tenant_access_token;
            $model->expired_at = date('Y-m-d H:i:s', TIMESTAMP + $data['expire'] - 10);
            $model->save();
        }else{
            $tenant_access_token = $item->tenant_access_token;
        }

        $this->sdk->setTenantAccessToken($tenant_access_token);

        return $tenant_access_token;
    }

    /**
     * @return array
     * 同步员工列表
     */
    public function syncEmployees(){
        // 设置 token
        $this->setTenantAccessToken();

        // 获取通讯录
        $contacts = $this->sdk->getContacts();

        // 无部门员工id
        $employeeIds = $contacts['authed_employee_ids'];

        $departmentsIds = $contacts['authed_departments'] ?? [];

        // 获取部门员工id
        foreach($departmentsIds as $departmentId){
            $departmentContacts = $this->sdk->getDepartmentContacts($departmentId);
            $departmentEmployeeIds = array_column($departmentContacts['user_list'], 'employee_id');
            $employeeIds = array_merge($employeeIds, $departmentEmployeeIds);
        }
        $employeeIds = array_unique($employeeIds);

        // 获取员工列表
        $data = $this->sdk->getEmployees($employeeIds);

        $employees = [];
        foreach($data['user_infos'] as $employee){
            $time = date('Y-m-d H:i:s', TIMESTAMP);
            $employee['created_at'] = $time;
            $employee['updated_at'] = $time;
            $employee['extends'] = [];
            $employee['extends'] = json_encode($employee['extends']);
            $employees[] = $employee;
        }

        // 批量插入
        $employeeModel = new FeishuEmployeeModel();
        $employeeModel->batchInsertOrUpdate($employees);

        return $employees;
    }

    /**
     * @param $string
     * @return array
     * 标签转富文本
     */
    private function tagToPost($string){
        preg_match_all("/<(.+?)>(.*?)<\/(.+?)>/", $string, $m);

        $post = [];
        foreach($m[3] as $k => $v){
            if($v == 'text'){
                $post[] = [
                    'tag' => 'text',
                    'text' => $m[2][$k]
                ];
            }elseif($v == 'at'){
                $post[] = [
                    'tag' => 'at',
                    'user_id' => '',
                    'name' => $m[2][$k],
                ];
            }elseif($v == 'a'){
                preg_match("/href=[\'\"](.*)[\'\"]/", $m[1][$k], $mm);
                $post[] = [
                    'tag' => 'a',
                    'text' => $m[2][$k],
                    'href' => $mm[1],
                ];
            }
        }
        return $post;
    }
}
